package lruchche;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author 12629
 * @Description：
 */
public class MyLRUCache {

    static class DLinkNode {
        public int key;
        public int val;
        public DLinkNode prev;
        public DLinkNode next;
        public DLinkNode() {

        }
        public DLinkNode(int key, int val) {
            this.key = key;
            this.val = val;
        }

        @Override
        public String toString() {
            return  "{ key=" + key +", val=" + val+"} ";
        }
    }

    public DLinkNode head;//双向链表的头节点
    public DLinkNode tail;//双向链表的尾巴节点
    public int usedSize;//代表当前双向链表当中 有效的数据个数
    public Map<Integer,DLinkNode> cache;//定义一个map
    public int capacity;//容量


    public MyLRUCache(int capacity) {
        this.head = new DLinkNode();
        this.tail = new DLinkNode();
        head.next = tail;
        tail.prev = head;
        cache = new HashMap<>();
        this.capacity = capacity;
    }

    /**
     * 存储元素
     * 1. 查找当前的这个key 是不是存储过
     * @param key
     * @param val
     */
    public void put(int key,int val) {
        //1. 查找当前的这个key 是不是存储过
        DLinkNode node = cache.get(key);
        //2. 如果没有存储过
        if(node == null) {
            //2.1 需要实例化一个节点
            DLinkNode dLinkNode = new DLinkNode(key,val);
            //2.2 存储到map当中一份
            cache.put(key,dLinkNode);
            //2.3 把该节点存储到链表的尾巴
            addToTail(dLinkNode);
            usedSize++;
            //2.4 检查当前双向链表的有效数据个数 是不是超过了capacity
            if(usedSize > capacity) {
                //2.5 超过了，就需要移除头部的节点
                DLinkNode remNode = removeHead();
                //2.6 清楚cache当中的元素
                cache.remove(remNode.key);
                //2.7 usedSize--;
                usedSize--;
            }
            printNodes("put");
        }else {
            //3. 如果存储过
            //3.1 更新这个key对应的value
            node.val = val;
            //3.2 然后将该节点，移动至尾巴处【因为这个是新插入的数据】
            moveToTail(node);
        }
    }

    /**
     * 移除当前节点到尾巴节点
     * 逻辑：先删除  后添加到尾巴
     * @param node
     */
    private void moveToTail(DLinkNode node) {
        //1. 先删除这个节点
        removeNode(node);
        //2. 添加到尾巴节点
        addToTail(node);
    }

    /**
     * 删除指定节点
     * @param node
     */
    private void removeNode(DLinkNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
    /**
     * 添加节点到链表的尾部
     * @param node
     */
    private void addToTail(DLinkNode node) {
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }

    private DLinkNode removeHead() {
        DLinkNode del = head.next;
        head.next = del.next;
        del.next.prev = head;
        return del;
    }


    /**
     * 访问当前的key
     *   逻辑：把你访问的节点 放到尾巴
     * @param key
     * @return
     */
    public int get(int key) {
        DLinkNode node = cache.get(key);
        if(node == null) {
            return -1;
        }
        //把最近 最多使用的 放到了链表的尾巴
        moveToTail(node);
        printNodes("get ");
        return node.val;
    }

    public void printNodes(String str) {
        System.out.println(str+": ");
        DLinkNode cur = head.next;
        while (cur != tail) {
            System.out.print(cur);
            cur = cur.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        MyLRUCache lruCache = new MyLRUCache(3);
        lruCache.put(100,10);
        lruCache.put(110,11);
        lruCache.put(120,12);
        System.out.println("获取元素");
        System.out.println(lruCache.get(110));
        System.out.println(lruCache.get(100));
        System.out.println("存放元素,会删除头节点，因为头节点是最近最少使用的: ");
        lruCache.put(999,99);
    }
}
